using System;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Web;
using Microsoft.VisualBasic;
using OFX.Aggregator.Infrastructure;

namespace OFX.Aggregator.Implementation.BESnet.Checking
{
	public sealed class StatementInterpreter : IStatementInterpreter
	{
		private readonly IRegexPatternProvider regexProvider;
		private readonly CultureInfo cultureInfo = CultureInfo.CurrentCulture;

		public StatementInterpreter(string culture, IRegexPatternProvider regexProvider)
		{
			this.regexProvider = regexProvider;
			this.cultureInfo = CultureInfo.GetCultureInfo(culture);
		}

		#region IStatementInterpreter Members

		public Statement InterpretStatement(string html)
		{
			NumberFormatInfo numberFormatInfo = this.cultureInfo.NumberFormat;

			const RegexOptions options = (RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase);
			Match matchStatementDate = Regex.Match(html, regexProvider.GetMatchPattern("StatementDate"), options);
			if (!matchStatementDate.Success)
				throw new Exception("Screen scraping failed");

			DateTime dateStatement = DateTime.Parse(matchStatementDate.Groups[1].Value, null, DateTimeStyles.AssumeLocal);
			Match matchStatementHeader = Regex.Match(html,
													 regexProvider.GetMatchPattern("StatementHeader"),
			                                         RegexOptions.Singleline);
			if (!matchStatementHeader.Success)
				throw new Exception("Screen scraping failed");

			string acctNumber = matchStatementHeader.Groups["AccountNumber"].Value;
			string currency = matchStatementHeader.Groups["Currency"].Value;
			Match matchAvailableCredit = Regex.Match(html, regexProvider.GetMatchPattern("AvailableCredit"), options);
			if (!matchAvailableCredit.Success)
				throw new Exception("Screen scraping failed");

			const double acctSpendingLimit = 0.0;
			double availableCredit = double.Parse(matchAvailableCredit.Groups[1].Value, NumberStyles.Currency, numberFormatInfo);

			var statement = new Statement
			                	{
			                		Currency = currency,
			                		Date = dateStatement,
			                		Account =
			                			{
			                				BankId = "BES",
			                				Number = acctNumber,
			                				Type = AccountType.Checking,
			                				SpendingLimit = acctSpendingLimit,
			                				Balance = (availableCredit - acctSpendingLimit)
			                			}
			                	};

			var checkingAcct = new PaymentOriginator {Id = acctNumber};

			var reg = new Regex(regexProvider.GetMatchPattern("StatementLine"), options);
			MatchCollection statementLineMatches = reg.Matches(html);
			foreach (Match statementLineMatch in statementLineMatches)
			{
				DateTime datePosted = DateTime.Parse(retrieveMatchValue(statementLineMatch, "datePosted"), null,
				                                     DateTimeStyles.AssumeLocal);
				datePosted = datePosted.AddHours(12);
				DateTime dateValue = DateTime.Parse(retrieveMatchValue(statementLineMatch, "dateValue"), null,
				                                    DateTimeStyles.AssumeLocal);
				dateValue = dateValue.AddHours(12);

				string transactionType = retrieveMatchValue(statementLineMatch, "transactionType");
				string description = retrieveMatchValue(statementLineMatch, "description");
				string debitAmount = retrieveMatchValue(statementLineMatch, "debitAmount");
				string creditAmount = retrieveMatchValue(statementLineMatch, "creditAmount");
				double controlBalance = double.Parse(retrieveMatchValue(statementLineMatch, "controlBalance"), NumberStyles.Currency,
				                                     numberFormatInfo);
				string transactionId = string.Format("{0}.{1}",
				                                     dateValue.ToUniversalTime().ToString("d", CultureInfo.InvariantCulture),
				                                     controlBalance.ToString(CultureInfo.InvariantCulture));

				double amount;
				if (debitAmount.Trim().Length > 1)
					amount = -double.Parse(debitAmount, NumberStyles.Currency, numberFormatInfo);
				else
					amount = double.Parse(creditAmount, NumberStyles.Currency, numberFormatInfo);

				if (description.ToUpper() == description)
				{
					description = Strings.StrConv(description, VbStrConv.ProperCase, this.cultureInfo.LCID);
				}

				var statementLine = new StatementLine
				                    	{
				                    		Amount = amount,
				                    		DatePosted = datePosted,
				                    		DateValue = dateValue,
				                    		Description = description,
				                    		Memo = description,
				                    		//OriginalCurrencyAmount = originalCurrencyAmount,
				                    		TransactionId = transactionId,
				                    		TransactionType = TransactionType.POS
				                    	};

				switch (transactionType)
				{
					case "LEV":
					case "DEB":
						statementLine.TransactionType = TransactionType.Debit;
						break;
					case "CRD":
						statementLine.TransactionType = TransactionType.Credit;
						break;
					case "SDD":
					case "PGT":
						statementLine.TransactionType = TransactionType.Payment;
						break;
					case "TRF":
						statementLine.TransactionType = TransactionType.Transfer;
						break;
					case "TPS":
						statementLine.TransactionType = TransactionType.POS;
						break;
				}

				checkingAcct.StatementLines.Add(statementLine);
			}

			statement.PaymentOriginators.Add(checkingAcct);

			StatementInterpreterHelper.InferStatementDates(statement);

			return statement;
		}

		#endregion

		private static string retrieveMatchValue(Match match, string captureName)
		{
			return HttpUtility.HtmlDecode(match.Groups[captureName].Value);
		}
	}
}